/*
    Copyright 2009 Carter Cheng

    This file is part of LPC on LLVM.

    LPC on LLVM is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    LPC on LLVM is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with LPC on LLVM.  If not, see <http://www.gnu.org/licenses/>.
*/


grammar LPC;
options 
{
	k=3; 
	backtrack=true; 
	memoize=true; 
	output=AST;
	ASTLabelType=pANTLR3_BASE_TREE;
	language = C;
}

tokens
{

//keywords
        ARRAY = 'array';
        BUFFER = 'buffer';
        BREAK = 'break';
        CASE = 'case';
        CATCH = 'catch';
	CLASS = 'class';
        CONTINUE = 'continue';
        DEFAULT = 'default';
        DO = 'do';
        EFUN = 'efun';
        ELSE = 'else';
        FLOAT = 'float';
        FOR = 'for';
        FOREACH = 'foreach';
        FUNCTION = 'function';
        IF = 'if';
        IN = 'in';
        INHERIT = 'inherit';
        INT = 'int';
        MAPPING = 'mapping';
        MIXED = 'mixed';
        NEW = 'new';
        NOMASK = 'nomask';
        NOSAVE = 'nosave';
        OBJECT = 'object';
	PARSE_COMMAND = 'parse_command';
        PRIVATE = 'private';
        PROTECTED = 'protected';
        PUBLIC = 'public';
        REF = 'ref';
        RETURN = 'return';
	SSCANF = 'sccanf';
        STATIC = 'static';
        STRING = 'string';
        SWITCH = 'switch';
	TIME_EXPRESSION = 'time_expression';
        WHILE = 'while'; 
        VARARGS = 'varargs';
        VOID = 'void';
	
// operators
        AND = '&';
        AND_ASSIGN = '&=';
        ASSIGN = '=';
        COLON = ':';
        COMMA = ',';
        DEC = '--';
        DIV = '/';
        DIV_ASSIGN = '/=';
        DOLLAR = '$';
        RANGE = '..';
        ELLIPSIS = '...';
        EQ = '==';
        GE = '>=';
        INC = '++';
        LBRACE = '{'; 
        LBRACKET = '[';
        LANGLE = '<';
        LE = '<=';
        LAND = '&&';
        LOR = '||';
        LPAREN = '(';
	LSH = '<<';
        LSH_ASSIGN = '<<=';
        MINUS = '-';
        MINUS_ASSIGN = '-=';
        MOD = '%';
        MOD_ASSIGN = '%=';
        MULT_ASSIGN = '*=';
        NEGATE = '~';
        NOT = '!';
        NE = '!=';
        OR = '|';
        OR_ASSIGN = '|=';
        PLUS = '+';
        PLUS_ASSIGN =  '+=';
        QUESTION = '?';
        RANGLE = '>';
        RARROW = '->';
        RBRACE = '}';
        RBRACKET = ']';
        RPAREN = ')';
        RSH = '>>';
        RSH_ASSIGN = '>>=';
        SEMI = ';';
        SCOPE = '::';
	STAR = '*';
        XOR = '^'; 
        XOR_ASSIGN = '^=';

	ARG_LIST;
	ARRAY_CON;
	ARRAY_ELEM;
        CONST;
	EXPR;
	FOR_EXPR;
	FUNCTIONAL_CON;
	FUNCTION_CON;
	FUNCTION_DECL;
	FUNCTION_DEF;
	LFUN;
	MAPPING_CON;
	MAPPING_ELEM;
        PROGRAM;
	STR_CONST;
	TYPE;
	TYPE_MOD;
	VARIABLES;
	VAR_DECL;
        VAR_INIT;
}
/*
@members {
public String getErrorMessage(RecognitionException e,
                               String[] tokenNames)
{
    List stack = getRuleInvocationStack(e, this.getClass().getName());
    String msg = null;
    if ( e instanceof NoViableAltException ) {
       NoViableAltException nvae = (NoViableAltException)e;
       msg = " no viable alt; token="+e.token+
           " (decision="+nvae.decisionNumber+
           " state "+nvae.stateNumber+")"+
           " decision=<<"+nvae.grammarDecisionDescription+">>";
    }
    else {
       msg = super.getErrorMessage(e, tokenNames);
    }
    return stack+" "+msg;
}
public String getTokenErrorDisplay(Token t) {
    return t.toString();
}
}
*/
program
	: declaration* EOF
		-> ^(PROGRAM declaration*)
	;

declaration
	: typeModifier? INHERIT stringConstant SEMI 
		-> ^(INHERIT typeModifier? stringConstant) 
	| typeModifier* type varInitList SEMI 
		-> ^(VARIABLES ^(TYPE_MOD typeModifier*) ^(TYPE type) varInitList)
	| typeModifier* functionType varName LPAREN formalDeclList? RPAREN SEMI
		-> ^(FUNCTION_DECL typeModifier* functionType varName formalDeclList?) 
 	| typeModifier* functionType varName LPAREN formalList? RPAREN statementBlock
		-> ^(FUNCTION_DEF typeModifier* functionType varName formalList? statementBlock)
	| CLASS IDENT LBRACE (varDecl SEMI)* RBRACE
		-> ^(CLASS IDENT varDecl+)
	;

statementBlock
	: LBRACE! statement* RBRACE!
	;

statement
	: statementBlock
	| type varInitList SEMI 
		-> ^(VAR_DECL type varInitList)
	| commaExpression SEMI!
	| IF^ LPAREN! expression RPAREN! statement ( (ELSE)=> ELSE! statement )?
	| SWITCH^ LPAREN! commaExpression RPAREN! switchBlock 
	| WHILE^ LPAREN! commaExpression RPAREN! statement 
	| DO^ statement WHILE! LPAREN! commaExpression RPAREN! SEMI!
	| FOR^ LPAREN! forExpression? SEMI commaExpression? SEMI commaExpression RPAREN! statement								
	| FOREACH^ LPAREN! (IDENT | varDecl) IN! commaExpression RPAREN! statement
	| FOREACH^ LPAREN! (IDENT | varDecl) COMMA! (IDENT | varDecl) IN! commaExpression RPAREN! statement
	| CONTINUE^ SEMI!
	| BREAK^ SEMI!
	| RETURN^ commaExpression? SEMI!
	| SEMI!
	;

forExpression
	: forExpressionElement (COMMA^ forExpressionElement )*
	;

forExpressionElement
	: varInit
	| expression
	;

commaExpression
	: expression (COMMA expression)*
		->^(EXPR expression+)			
	;
	
expression
	: conditionalExpression ((ASSIGN^|MULT_ASSIGN^|DIV_ASSIGN^|MOD_ASSIGN^|PLUS_ASSIGN^|MINUS_ASSIGN^|LSH_ASSIGN^|RSH_ASSIGN^|AND_ASSIGN^|XOR_ASSIGN^|OR_ASSIGN^) expression)?
	;
	
conditionalExpression
	: logicalOrExpression (QUESTION^ commaExpression COLON! conditionalExpression)?
	;

logicalOrExpression
	: logicalAndExpression (LOR^ logicalAndExpression)*
	;

logicalAndExpression
	: inclusiveOrExpression (LAND^ inclusiveOrExpression )*
	;

inclusiveOrExpression
	: exclusiveOrExpression (OR^ exclusiveOrExpression )*
	;

exclusiveOrExpression
	: andExpression (XOR^ andExpression )*
	;

andExpression
	: equalityExpression (AND^ equalityExpression )*
	;
equalityExpression
	: relationalExpression ( (EQ^|NE^) relationalExpression )*
	;

relationalExpression
	: shiftExpression ( (LANGLE^|RANGLE^|LE^|GE^) shiftExpression )*
	;

shiftExpression
	: additiveExpression ( (LSH^|RSH^) additiveExpression )*
	;

additiveExpression
	: multiplicativeExpression ( (PLUS^|MINUS^) multiplicativeExpression )*
	;

multiplicativeExpression
	: castExpression ( (STAR^|DIV^|MOD^) castExpression )*
	;

castExpression
	: LPAREN type (STAR)* RPAREN castExpression
	| unaryExpression
	;
	
unaryExpression
	: postfixExpression
	| (INC^|DEC^) unaryExpression
	| (MINUS^|NEGATE^|NOT^) castExpression
	| SSCANF^ LPAREN! expression COMMA! expression (COMMA! unaryExpression)* RPAREN!
	| PARSE_COMMAND^ LPAREN! expression COMMA! expression COMMA! expression (COMMA! unaryExpression)* RPAREN!
	| TIME_EXPRESSION^ LPAREN! commaExpression RPAREN!
	| TIME_EXPRESSION^ statementBlock
	;

postfixExpression
	: primaryExpression
	( RARROW^ IDENT (LPAREN! argumentList? RPAREN!)? 
	| LBRACKET^ LANGLE? commaExpression RANGE LANGLE? commaExpression RBRACKET
	| LBRACKET^ LANGLE? commaExpression RANGE RBRACKET
	| LBRACKET^ LANGLE? commaExpression RBRACKET
	| INC^
	| DEC^
	)*
	;

primaryExpression
	: constant
	| IDENT^
	| PARAM^	
        | EFUN^ SCOPE! scopedIdent LPAREN! argumentList? RPAREN!
	| EFUN^ SCOPE! NEW LPAREN! argumentList? RPAREN!
	| scopedIdent LPAREN argumentList? RPAREN
		->^(LFUN scopedIdent argumentList?)
	| LPAREN! STAR^ commaExpression RPAREN! LPAREN! argumentList? RPAREN!
	| NEW^ LPAREN! argumentList? RPAREN!
	| NEW^ LPAREN! CLASS IDENT ( COMMA! classInitList)? RPAREN!
	| DOLLAR^ LPAREN! commaExpression RPAREN!
	| LPAREN! commaExpression RPAREN! 
	| LPAREN LBRACE { $LPAREN.index == $LBRACE.index - 1 }? arrayElementList? RBRACE RPAREN { $RBRACE.index == $RPAREN.index - 1 }?
		-> ^(ARRAY_CON arrayElementList?)
	| FUNCTION LPAREN formalList? RPAREN statementBlock
		-> ^(FUNCTION_CON formalList? statementBlock)
	| LPAREN COLON commaExpression COLON RPAREN
		-> ^(FUNCTIONAL_CON commaExpression)
	| LPAREN LBRACKET { $LPAREN.index == $LBRACKET.index - 1 }? mappingElementList? RBRACKET RPAREN { $RBRACKET.index == $RPAREN.index - 1 }?
		-> ^(MAPPING_CON mappingElementList?)
	| CATCH^ LPAREN! commaExpression RPAREN!
	| CATCH^ statementBlock
	;

classInitList
	: classInit (COMMA! classInit)*
	;

classInit
	: IDENT COLON expression
	;

scopedIdent
	: SCOPE? IDENT -> ^(IDENT)
	| IDENT SCOPE! IDENT^
	| basicType SCOPE! IDENT^
	;
	
constantExpression 
	: expression
		-> ^(CONST expression)
	;
	
stringConstant
	: constantExpression
		-> ^(STR_CONST constantExpression) 
	;
	
varDecl
	: type varName
	;	

formalDeclList
	: formalDecl (COMMA^ formalDecl)* ELLIPSIS?
	;
	
formalDecl
	: formalType varName
	| formalType STAR* 
	;

formalList
	: formal (COMMA^ formal)* ELLIPSIS?
	;
	
formal 
	: formalType varName
	; 

switchBlock
	:  LBRACE! (type varInitList SEMI)* switchGroup* RBRACE!
	;
	
switchGroup
	: caseGuard statement*
	;
caseGuard
	: CASE caseExpression COLON
	| CASE caseExpression RANGE caseExpression COLON
	| CASE caseExpression RANGE COLON
	| CASE RANGE caseExpression COLON
	| DEFAULT COLON
	;

caseExpression
	: constantExpression
	;
	
argumentList
	: expression (COMMA^ expression)* ELLIPSIS?
	; 	
		
arrayElementList 
	: arrayElement (COMMA^ arrayElement)* COMMA? 
	;

arrayElement
	: expression ELLIPSIS? 
		-> ^(ARRAY_ELEM expression ELLIPSIS?)
	;
	
mappingElementList
	: mappingElement (COMMA^ mappingElement)* COMMA?
	;
	
mappingElement
	: expression COLON expression
		-> ^(MAPPING_ELEM expression expression)
	;

varInitList
	: varInit (COMMA varInit)*
		-> varInit+
	;

varInit
	: varName (ASSIGN expression)?
		->^(VAR_INIT varName expression?) 
	;
	
varName	
	: STAR* IDENT
	; 

typeModifier
	: NOMASK
	| NOSAVE
	| PRIVATE
	| PROTECTED
	| PUBLIC
	| STATIC
	| VARARGS			
	;

formalType
	: ARRAY
	| MAPPING
	| basicType REF? ARRAY?
	;

functionType
	: VOID
	| type
	;

type
	: ARRAY
	| MAPPING
	| basicType ARRAY?
	;

basicType
	: BUFFER
	| CLASS IDENT
	| FLOAT
	| FUNCTION
	| INT
	| MIXED
	| OBJECT 
	| STRING
	;
	
constant
	: CHAR_LITERAL
	| STRING_LITERAL (STRING_LITERAL)*
	| DEC_LITERAL
	| HEX_LITERAL
	| FLOAT_LITERAL
	;											

IDENT
	: ( '_' |'a' .. 'z' | 'A' .. 'Z' ) ('_' | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9')*
	; 
PARAM	
	: '$' '1' .. '9' '0' .. '9' *
	;

STRING_LITERAL
	: '"' ( ( '\\' ('b'|'t'|'n'|'r'|'\"'|'\''|'\\') ) | ~('\\'|'"') )* '"'
    	;

CHAR_LITERAL
	: '\'' ( ( '\\' ('b'|'t'|'n'|'r'|'\"'|'\''|'\\') ) | ~('\\'|'\'') ) '\''   
	;

DEC_LITERAL	
	: ( '0' | '1' .. '9' '0' .. '9'*) ( ('.' ~'.') => '.' '0' .. '9'* { $type = FLOAT_LITERAL; } )? 
	;
HEX_LITERAL	
	: '0' ('x' | 'X' ) ('0'..'9'|'a'..'f'|'A'..'F') 
	;
	
FLOAT_LITERAL
	: '.' '0' .. '9'+
	;	
	
WHITE_SPACE  
	: ( (' '|'\r'|'\t'|'\n') 
	  | ('/*' ( options {greedy=false;} : . )* '*/') 
	  | ('//' ~('\n'|'\r')* '\r'? '\n') 
	  | ('#' ~('\n'|'\r')* '\r'? '\n') 
          ) {$channel=HIDDEN;}
   	;
